import fs from 'fs-extra';
import fg from 'fast-glob';
import { toolchain } from "../toolchain.js";
import { UnityHelper } from './UnityHelper.js';
import { Record } from './Record.js';
import { Relatives } from './Relatives.js';
import path from 'path';
import { Project } from 'ts-morph';
import { UIRelations } from './UIRelations.js';
import sharp from 'sharp';
import inquirer from 'inquirer';

export class Handyman {
    public async executeDelete(r: Relatives): Promise<boolean> {
        if (!toolchain.opts.input) {
            console.error('no input file!');
            return false;
        }

        const content = await fs.readFileSync(toolchain.opts.input, 'utf-8');
        const toBeDeleteds = content.split(/\r?\n/).map(d => d.split(' ')[0]);
        for (const d of toBeDeleteds) {
            if (!fs.existsSync(d)) continue;

            await r.collect(d);
            const guid = await UnityHelper.readGUID(d + '.meta');
            await fs.unlink(d);
            await fs.unlink(d + '.meta');
            await Record.Instance.recordGUID(guid, d);
        }

        return toBeDeleteds.find(d => d.endsWith('.prefab')) != null;
    }

    public async replaceFile(): Promise<void> {
        const a = path.join(toolchain.opts.projectRoot, toolchain.opts.input!);
        const b = path.join(toolchain.opts.projectRoot, toolchain.opts.target!);
        let modifySize = false;
        if (a.endsWith('.png') || a.endsWith('.jpg')) {
            const answer = await inquirer.prompt([{
                type: 'input',
                name: 'yesorno',
                message: 'Shall we modify the picture size? (Y/N)',
            }]);
            modifySize = answer.yesorno == 'Y';
        }

        const guidA = await UnityHelper.readGUID(a + '.meta');
        const guidB = await UnityHelper.readGUID(b + '.meta');
        const targets = await fg(UnityHelper.CompositeFileTypes.map((v) => `**/*${v}`), { cwd: toolchain.opts.projectRoot });
        for (const tgt of targets) {
            const tgtf = path.join(toolchain.opts.projectRoot, tgt);
            const content = await fs.readFile(tgtf, 'utf-8');
            const newContent = content.replaceAll(guidA, guidB);
            if (newContent != content) {
                // 修改按钮尺寸
                if (modifySize) {
                    const sa = sharp(a);
                    const imgMetaA = await sa.metadata();
                    const sizeAStr = `m_SizeDelta: {x: ${imgMetaA.width}, y: ${imgMetaA.height}}`;
                    const sb = sharp(b);
                    const imgMetaB = await sb.metadata();
                    const sizeBStr = `m_SizeDelta: {x: ${imgMetaB.width}, y: ${imgMetaB.height}}`;
                    const newContentB = newContent.replaceAll(sizeAStr, sizeBStr);
                    if (newContentB == newContent) {
                        console.error('image replaced but size not modified!', tgt);
                    }
                    await fs.writeFile(tgtf, newContentB, 'utf-8');                    
                } else {
                    await fs.writeFile(tgtf, newContent, 'utf-8');
                }
            }
        }
        await fs.unlink(a);
        await fs.unlink(a + '.meta');
    }

    public async anyway(): Promise<void> {
        await this.findPotentialBugs();
    }

    public async fixWindowAnim(): Promise<void> {
        const windowAnimController = path.join(toolchain.opts.projectRoot, 'Assets/AssetSources/ui/animation/windowOpen/windowAnim.controller');
        const waguid = await UnityHelper.readGUID(windowAnimController + '.meta');
        const root = path.join(toolchain.opts.projectRoot, 'Assets/AssetSources/ui');
        const tsRoot = path.join(toolchain.opts.projectRoot, 'TsScripts');
        const prefabs = await fg('**/*.prefab', { cwd: root });
        for (const p of prefabs) {
            const file = path.join(root, p);
            const content = await fs.readFile(file, 'utf-8');
            if (content.includes(waguid) && !content.includes('CanvasGroup:')) {
                const pn = path.basename(p, '.prefab');
                const tsfs = await fg(pn + '.ts', { cwd: tsRoot, caseSensitiveMatch: false });
                let useAlpha = undefined;
                if (tsfs.length > 0) {
                    const tsf = path.join(tsRoot, tsfs[0]);
                    const tsContent = await fs.readFile(tsf, 'utf-8');
                    const wpan = tsContent.matchAll(/this\._windowOpenAnimName\s=\s("|')(\w+)\1/g);
                    const wcan = tsContent.matchAll(/this\._windowCloseAnimName\s=\s("|')(\w+)\1/g);
                    for (const n of wpan) {
                        if (n[2] == 'windowOpen') {
                            useAlpha = true;
                        } else {
                            useAlpha = false;
                        }
                    }
                    for (const n of wcan) {
                        if (n[2] == 'windowClose') {
                            useAlpha = true;
                        } else {
                            useAlpha = false;
                        }
                    }
                }
                if (useAlpha != false) {
                    console.error(pn);
                }
            }
        }
    }

    private async fixMonster(): Promise<void> {
        const matRoot = path.join(toolchain.opts.projectRoot, 'Assets/AssetSources/modelSource/monster');
        const files = await fg('**/*.mat', { cwd: matRoot });
        for (const f of files) {
            const matf = path.join(matRoot, f);
            const content = await fs.readFile(matf, 'utf-8');
            if (content.includes('7746c211250e33a419416482fc37989c')) {
                let newContent = content.replace('_EdgeWidth: 0.1', '_EdgeWidth: 0.05')
                // .replace('_EdgeColor: {r: 1, g: 1, b: 1, a: 1}', '_EdgeColor: {r: 0.23137252, g: 0.34346527, b: 0.9607843, a: 1}');
                // if (!newContent.includes('_EdgeColor')) {
                //     const lines = newContent.split(/\r?\n/);
                //     const idx = lines.findIndex(v => v.includes('_CurvedWorldBendSettings'));
                //     if (idx >= 0) {
                //         lines.splice(idx + 1, 0, '    - _EdgeColor: {r: 0.23137252, g: 0.34346527, b: 0.9607843, a: 1}');
                //     }
                //     newContent = lines.join('\n');
                // }
                await fs.writeFile(matf, newContent, 'utf-8');
            }
        }
    }

    private async findMissCfg(): Promise<void> {
        const a = 'D:/works/dp_minigame/trunk/Assets/AssetSources/data';
        const aCfgs = await fg('*.json', { cwd: a });
        const b = 'D:/works/dpxyx/trunk/project/Assets/AssetSources/data';
        const bCfgs = await fg('*.json', { cwd: b });

        for (const c of aCfgs) {
            if (!bCfgs.includes(c)) {
                console.error(c);
            }
        }
    }

    private async findPotentialBugs(): Promise<void> {
        await UIRelations.build();
        const allForms = UIRelations.getAllForms();

        const tsRoot = path.join(toolchain.opts.projectRoot, 'TsScripts');

        const project = new Project({
            tsConfigFilePath: path.join(tsRoot, 'tsconfig.json')
        });

        for (const f of allForms) {
            const src = project.getSourceFile(f.tsClass.sourceFile)!;
            if (!src) {
                console.error('SourceFile not exists:', f.tsClass.sourceFile);
                continue;
            }
            const classes = src.getClasses();
            for (const cls of classes) {
                if (cls.getName() != f.tsClass.className) continue;
                if (cls.getConstructors()[0]?.getText().includes('this._cacheForm = true')) continue;
                const mthds = cls.getMethods();
                for (const mthd of mthds) {
                    const body = mthd.getBody();
                    if (body == null) continue;

                    const startLine = body.getStartLineNumber();

                    const lines = body.getText().split(/\r?\n/);

                    let closeLine = -1, lineCnt = lines.length;
                    for (let i = 0; i < lineCnt; i ++) {
                        const line = lines[i];
                        if (!line.match(/^\s*\/\//) && (line.includes('.doClose()') || line.includes('.close()'))) {
                            closeLine = i;
                            break;
                        }
                    }
                    if (closeLine >= 0 && closeLine < lineCnt - 1) {
                        const nextLine = lines[closeLine + 1];
                        if (nextLine.includes('return')) continue;
                        for (let i = closeLine + 1; i < lineCnt; i ++) {
                            const line = lines[i];
                            if (line.includes('this.')) {
                                console.error(`try to access properties after dispose: ${f.tsClass.sourceFile}:${startLine + i}`);
                                console.error(line);
                                break;
                            }
                        }
                    }
                }
            }
        }
    }
}
